#include "edaSA.h"

#ifdef _DEBUG_
#include <iostream>
using namespace std;
#endif

edaSA::edaSA()
  : move(NULL),
    moveRandom(NULL),
    continueCriteria(NULL),
    initTemperature(1e100),
    coolingSchedule(NULL)
{
}

edaSA::edaSA( edaMove *_move, 
	      edaMoveGen *_moveRandom, 
	      edaContinue *_continueCriteria,
	      double _initTemperature, 
	      edaCoolingSchedule *_coolingSchedule ) 
  : initTemperature (_initTemperature)
{
  move = _move->clone();
  moveRandom = _moveRandom->clone();
  continueCriteria = _continueCriteria->clone();
  coolingSchedule = _coolingSchedule->clone();
}

edaSA::~edaSA()
{
  delete move;
  delete moveRandom;
  delete continueCriteria;
  delete coolingSchedule;
}

bool edaSA::search(edaSolution &sol)
{
  double temp = initTemperature;
  edaSolution *bestSolution = sol.clone();

  do
  {
    continueCriteria->init();

    do
    {
      moveRandom->generate(move, sol);
      double newFitness = move->incrEval(sol);
      double fitnessDiff = newFitness - sol.fullEvaluate();

      printDebug(6, "newFitness: " << newFitness << ", diff: " << fitnessDiff);

      // Better solution OR worse solution (in some case)
      if ((fitnessDiff > 0) || (rnd.uniform() < exp(fitnessDiff / temp)))
      {
        move->update(sol);

        // Update best solution found
        if (bestSolution->fullEvaluate() < sol.fullEvaluate())
        {
          *bestSolution = sol;
          printDebug(6, "fit: " << bestSolution->fullEvaluate() << ", bestSolution: " << *bestSolution);
        }
      }
    } while (continueCriteria->check(sol));
  } while (coolingSchedule->check(temp));

  sol = *bestSolution;

  delete bestSolution;
  return true;
}

void edaSA::Serialize( edaBuffer &buf, bool pack )
{
  if ( pack ){
    move->doSerialize( buf, pack );
    moveRandom->doSerialize( buf, pack );
    continueCriteria->doSerialize( buf, pack );
    coolingSchedule->doSerialize( buf, pack );
    buf.Pack( &initTemperature, 1 );
  } else {
    move = (edaMove*)classGenerateFromBuffer( buf );
    moveRandom = (edaMoveGen*)classGenerateFromBuffer( buf );
    continueCriteria = (edaContinue*)classGenerateFromBuffer( buf );
    coolingSchedule = (edaCoolingSchedule*)classGenerateFromBuffer( buf );
    buf.UnPack( &initTemperature, 1 );
  }
}
